<?php
/**
 * @author andery <andery@foxmail.com>
 * @link http://www.minophp.com/
 * @version $Id: CApplication.php 2009-07-25 22:20:11Z andery $
 */
class CApplication {
	public $charset = 'UTF-8';
    private static $_langModel = array();
    private $_apppath;
    private $_sys_view_path;
    private $_controller;
    private $_action;
    private $_appurl;
    private $_urlparams;

    public function __construct($config=null)
    {
        // 加载配置文件
        CConf::getInstance($config);
        // 保存应用程序目录信息
        $this->_setAppPath(CConf::get('system.apppath'));
        // 设置编码
        $this->charset = CConf::get('system.charset');
        // 设置错误页面
        $this->_setSysViewPath();
        // 设置异常处理
        $this->setHandlers();
        // 加载核型语言文件
        CLang::initLang();
        // 应用程序注册
        Nimo::setApp($this);
        // 加载外部扩展库
        $this->loadExtlib();
    }
    
    public function getAppPath()
    {
        return $this->_apppath;
    }
    
    public function getAppUrl()
    {
        return $this->_appurl;
    }
    
    public function getController()
    {
        return $this->_controller;
    }
    
    public function getAction()
    {
        return $this->_action;
    }
    
    public function get_UrlParams()
    {
        return $this->_urlparams;
    }
    
    public function create_url($route, $params=array())
    {
        return $this->processRequest()->create_url($route, $params);
    }
    
    private function _setAppPath($path)
    {
        if (($this->_apppath=realpath($path))===false || !is_dir($this->_apppath)) {
            throw new CException(CLang::get('apppath_not_exists'));
        }
    }
    
    /**
     * 设置系统页面模板
     */
    private function _setSysViewPath()
    {
        $this->_sys_view_path = $this->getAppPath() . CConf::get('system.system_view') . '/';
    }
    
    /**
     * 启动应用程序
     */
    public function start()
    {
        $route = $this->processRequest();
        $this->_controller = $route->getController();
        $this->_action = $route->getAction();
        $this->_appurl = $route->getAppUrl();
        $this->_urlparams = $route->getParams();
        $controller_class = $this->_controller.'Controller';
        if (!is_file($this->getAppPath() . CConf::get('system.control_path') . '/' . $controller_class . '.php')){
            throw new CHttpException('404', CLang::get('controller_not_exists'));
        } else {
            include_once $this->getAppPath() . CConf::get('system.control_path') . '/' . $controller_class . '.php';
        }
        $controller = new $controller_class($route->getParams());
        $action = 'on_' . $this->_action;
        if (!method_exists($controller_class, $action)) {
            throw new CHttpException('404', CLang::get('action_not_exists'));
        } else {
            $controller->$action();
        }
    }
    
    /**
     * 加载应用程序外部扩展库
     */
    private function loadExtlib()
    {
        $external_libs = @include_once(CConf::get('system.external_libs'));
        if ($external_libs=='' || !is_array($external_libs)) {
            return;
        }
        foreach ($external_libs as $lib) {
            require($this->getAppPath() . $lib);
        }
    }
    
    /**
     * 请求处理
     */
    public function processRequest()
    {
        return new CUrlManager();
    }
    
    /**
     * 加载模型
     * @param string 模型名称
     */
    public function loadModel($model, $show_error=true)
    {
        if (empty($_ENV[$model])) {
            if (file_exists($this->getAppPath() . CConf::get('system.model_path') . '/' . $model.'Model.php')){
                require_once $this->getAppPath() . CConf::get('system.model_path') . '/' . $model.'Model.php';
                eval('$_ENV[$model] = new '.$model.'Model();');    
                return $_ENV[$model];
            }else{
                if ($show_error) {
                    throw new CException(CLang::get('model_not_exists'));
                } else {
                    return false;
                }
            }
        } else {
            return $_ENV[$model];
        }
    }
    
    /**
     * 分析代码
     */
    protected function getSourceLines($file,$line)
    {
        $maxLines=25;
        $line--;
        if ($line<0 || ($lines=@file($file))===false || ($lineCount=count($lines))<=$line) {
            return array();
        }
        $halfLines=intval($maxLines/2);
        $beginLine=$line-$halfLines>0?$line-$halfLines:0;
        $endLine=$line+$halfLines<$lineCount?$line+$halfLines:$lineCount-1;
        $sourceLines=array();
        for ($i=$beginLine;$i<=$endLine;++$i) {
            $sourceLines[$i+1]=$lines[$i];
        }
        return $sourceLines;
    }
    
    /**
     * 输出异常
     */
    private function displayException($exception)
    {
        $traceInfo='';
        $trace = $exception->getTrace();
        foreach($trace as $t) {
        	$time = date("y-m-d H:i:m");
            $traceInfo .= '['.$time.'] ';
            $traceInfo .= (!empty($t['file']) && !empty($t['line'])) ? $t['file'] . ' (' . $t['line'] . ') ' : '';
            $traceInfo .= !empty($t['class']) ? $t['class'] : '';
            $traceInfo .= !empty($t['type']) ? $t['type'] : '';
            $traceInfo .= !empty($t['function']) ? $t['function'] : '';
            $traceInfo .= '(';
            if (!empty($t['args'])) {
                foreach ($t['args'] as $ta) {
                    if (is_array($ta)) {
                        $traceInfo .= 'array, ';
                    } else {
                        $traceInfo .= $ta.', ';
                    }
                }
                $traceInfo = substr($traceInfo, 0, -2);    
            }    
            $traceInfo .=")\n";
        }
        $error['code'] = ($exception instanceof CHttpException||$exception instanceof CDbException)?$exception->statusCode:500;
        $error['trace'] = $traceInfo;
        $error['type'] = get_class($exception);
        $error['message'] = $exception->getMessage();
        $error['file'] = $exception->getFile();
        $error['line'] = $exception->getLine();
        $error['source']    = $this->getSourceLines($error['file'],$error['line']);

        if ($exception instanceof CHttpException && !NM_DEBUG) {
            if (is_file($this->_sys_view_path . 'error' . $error['code'] . '.php')) {
                include_once($this->_sys_view_path . 'error' . $error['code'] . '.php');
            } else {
                include_once(NM_VIEW_PATH . 'error' . $error['code'] . '.php');
            }
        } elseif ($exception instanceof CDbException && !NM_DEBUG) {
            $error['db_type'] = $exception->db_type;
            if (is_file($this->_sys_view_path . 'db_error.php')) {
                include_once($this->_sys_view_path . 'db_error.php');
            } else {
                include_once(NM_VIEW_PATH . 'db_error.php');
            }
        } else {
            if (is_file($this->_sys_view_path . 'exception.php')) {
                include_once($this->_sys_view_path . 'exception.php');
            } else {
                include_once(NM_VIEW_PATH . 'exception.php');
            }
        }
        die();
    }
    
    /**
     * 输出错误
     */
    private function displayError($code,$message,$file,$line)
    {
        $trace=debug_backtrace();
        if (count($trace)>3) $trace=array_slice($trace,3);
        $traceString='';
        foreach ($trace as $i=>$t) {
            if (!isset($t['file'])) $t['file']='unknown';
            if (!isset($t['line'])) $t['line']=0;
            if (!isset($t['function'])) $t['function']='unknown';
            $traceString.="#$i {$t['file']}({$t['line']}): ";
            if (isset($t['object']) && is_object($t['object'])) $traceString.=get_class($t['object']).'->';
            $traceString.="{$t['function']}()\n";
        }
        $app = Nimo::app();
        if ($app instanceof CApplication) {
            $error=array(
                'code'=>500,
                'type'=>'PHP Error',
                'message'=>$message,
                'file'=>$file,
                'line'=>$line,
                'trace'=>$traceString,
                'source'=>$this->getSourceLines($file,$line),
            );
            if (!headers_sent()) header("HTTP/1.0 500 PHP Error");
            if (NM_DEBUG) {
                if (is_file($this->_sys_view_path . 'exception.php')) {
                    include_once($this->_sys_view_path . 'exception.php');
                } else {
                    include_once(NM_VIEW_PATH . 'exception.php');
                }
            } else {
                if (is_file($this->_sys_view_path . 'error500.php')) {
                    include_once($this->_sys_view_path . 'error500.php');
                } else {
                    include_once(NM_VIEW_PATH . 'error500.php');
                }
            }
        } else {
            if (is_file($this->_sys_view_path . 'error.php')) {
                include_once($this->_sys_view_path . 'error.php');
            } else {
                include_once(NM_VIEW_PATH . 'error.php');
            }
        }
        die();
    }
    
    /**
     * 抛出异常并显示异常信息
     */
    public function handleException($exception)
    {
        $this->restoreHandlers();
        $this->displayException($exception);
    }
    
    /**
     * 抛出错误并显示错误信息
     */
    public function handleError($code,$message,$file,$line)
    {
        $this->restoreHandlers();
        $this->displayError($code,$message,$file,$line);
    }
    
    /**
     *  恢复默认异常处理 
     */
    public function restoreHandlers()
    {
        restore_error_handler();
        restore_exception_handler();
    }
    
    /**
     * 自定义异常处理
     */
    public function setHandlers()
    {
        set_exception_handler(array($this,'handleException'));
        set_error_handler(array($this,'handleError'), error_reporting());
    }
}